package com.evaluation_system.service.impl;

import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.evaluation_system.Util.PdfEncryptor;
import com.evaluation_system.Util.PdfSM4Encryptor;
import com.evaluation_system.exception.*;
import com.evaluation_system.pojo.DTO.EvaluatorPageQueryDTO;
import com.evaluation_system.pojo.DTO.FilePageQueryDTO;
import com.evaluation_system.pojo.Result.PageResult;
import com.evaluation_system.pojo.entity.Evaluator;
import com.evaluation_system.pojo.entity.Uploader;
import com.evaluation_system.pojo.entity.saveFileInfo;
import com.evaluation_system.service.EvaluatorService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.fisco.bcos.sdk.BcosSDK;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair;
import org.fisco.bcos.sdk.transaction.manager.AssembleTransactionProcessor;
import org.fisco.bcos.sdk.transaction.manager.TransactionProcessorFactory;
import org.fisco.bcos.sdk.transaction.model.dto.TransactionResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.core.io.InputStreamResource;
import com.evaluation_system.mapper.EvaluatorMapper;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;

import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import com.aliyun.oss.*;
import org.springframework.transaction.annotation.Transactional;

import java.net.URL;

import java.util.Date;
import org.springframework.beans.factory.annotation.Value;
@Service
public class EvaluatorServiceImpl implements EvaluatorService {

    @Autowired
    EvaluatorMapper evaluatormapper;

    @Value("${oss.bucket-name}")
    private String bucketName;
//    @Override
//    public List<saveFileInfo> readFile(int size,int page,String filename) {
//        try{
//            if(filename==null || filename.equals(""))
//                return evaluatormapper.getFile(1,size,(page-1)*size);
//            else
//                return evaluatormapper.getOneFile(filename);
//        } catch (Exception e) {
//           throw new zeroPageException("页面为0异常");
//        }
//
//    }
    @Value("${decrypt.output-dir}")
    private String decryptOutputDir;


    @Override
    public PageResult<saveFileInfo> readFile(FilePageQueryDTO filePageQueryDTO) {
        // 分页查询
        PageHelper.startPage(filePageQueryDTO.getPage(), filePageQueryDTO.getPageSize());
        // 执行查询
        Page<saveFileInfo> page = evaluatormapper.getFile1(filePageQueryDTO);
        return new PageResult<>(page.getTotal(), page.getResult());
    }



    @Override
    public byte[] getDownloadpdf(Integer id) {
        byte[] pdfBytes=new byte[99999];
        try{
            // Endpoint以华东1（杭州）为例，其它Region请按实际情况填写。
            String endpoint = "https://oss-cn-beijing.aliyuncs.com";
            // 从环境变量中获取访问凭证。运行本代码示例之前，请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
//            // 填写Bucket名称.
//            String bucketName = "bucketName";
            // 填写Object完整路径，完整路径中不能包含Bucket名称，例如exampledir/exampleobject.txt。
            Integer userId = evaluatormapper.getId(id);
            String objectName = userId.toString()+"/"+evaluatormapper.getFileName(id)+".pdf";
            // 填写Bucket所在地域。
            String region = "cn-beijing";

            // 创建OSSClient实例。
            // 当OSSClient实例不再使用时，调用shutdown方法以释放资源。
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region(region)
                    .build();

            try {
                // ossObject包含文件所在的存储空间名称、文件名称、文件元数据以及一个输入流。
                OSSObject ossObject = ossClient.getObject(bucketName, objectName);
                InputStream inputStream = ossObject.getObjectContent();
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

                int bytesRead;
                while ((bytesRead = inputStream.read(pdfBytes)) != -1) {
                    byteArrayOutputStream.write(pdfBytes, 0, bytesRead);
                }
                // 获取最终的字节数组。
                byte[] fileBytes = byteArrayOutputStream.toByteArray();
                // 打印字节数组的长度。
                System.out.println("Downloaded file size: " + fileBytes.length + " bytes");
                // 数据读取完成后，获取的流必须关闭，否则会造成连接泄漏，导致请求无连接可用，程序无法正常工作。
                inputStream.close();
                byteArrayOutputStream.close();
                // ossObject对象使用完毕后必须关闭，否则会造成连接泄漏，导致请求无连接可用，程序无法正常工作。
                ossObject.close();
                return fileBytes;
            } catch (OSSException oe) {
                System.out.println("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                System.out.println("Error Message:" + oe.getErrorMessage());
                System.out.println("Error Code:" + oe.getErrorCode());
                System.out.println("Request ID:" + oe.getRequestId());
                System.out.println("Host ID:" + oe.getHostId());
            } catch (Throwable ce) {
                System.out.println("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                System.out.println("Error Message:" + ce.getMessage());
            } finally {
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }
        }
        catch (Exception e) {
            System.err.println("执行失败: " + e.getMessage());
            throw new downloadFailException("下载失败");
        }

        return pdfBytes;
    }

    @Override
    public byte[] decryptionPdf(byte[] pdfBytes,Integer id) {

        try {
            String secretKeyStr = evaluatormapper.getSecretKey(id);
            String ivstr = evaluatormapper.getSecretIV(id);

            byte[] decodedKey = Base64.getDecoder().decode(secretKeyStr);
            SecretKey secretKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES"); // 替换

            byte[] decodeiv= Base64.getDecoder().decode(ivstr);
            IvParameterSpec iv = new IvParameterSpec(decodeiv);

//            byte[] ivBytes = Base64.getDecoder().decode(ivstr);
//
//
//            IvParameterSpec restoredIv = new IvParameterSpec(ivBytes);

//            byte[] decryptedBytes = PdfSM4Encryptor.decrypt(pdfBytes, secretKey,iv);
//            Path decryptedPath = Paths.get("encrypted_files", "解密1234.pdf");
//            Files.write(decryptedPath, decryptedBytes);

            byte[] decryptedBytes = PdfSM4Encryptor.decrypt(pdfBytes, secretKey, iv);

// ✅ 创建路径
            Path decryptedDir = Paths.get(decryptOutputDir);
//            Files.createDirectories(decryptedDir);

// ✅ 拼接文件路径
            Path decryptedPath = decryptedDir.resolve("解密1234.pdf");
            Files.write(decryptedPath, decryptedBytes);

            // Path filePath = Paths.get("/opt/files/report.pdf");

            // 2. 构建文件资源
//            InputStreamResource resource = new InputStreamResource(
//                    Files.newInputStream(decryptedPath)
//            );
//            System.out.println("0722file is "+ resource);
            System.out.println(decryptedPath);
            System.out.println(decryptedBytes);
            return decryptedBytes ;
        }
        catch (Exception e){
            e.printStackTrace();
            throw new decryptedFailException("解密失败");
        }

//        return filePdf;

    }

    @Override
    public String getDownloadUrl(Integer id ,byte[] pdfBytes) {
        try{
            String endpoint = "https://oss-cn-beijing.aliyuncs.com";
            // 从环境变量中获取访问凭证。运行本代码示例之前，请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
            // 填写Bucket名称.
            String bucketName = "downloadsafe";
            // 填写Object完整路径，完整路径中不能包含Bucket名称，例如exampledir/exampleobject.txt。
            String objectName = id.toString()+"/"+"saveDownload"+".pdf";
            // 填写Bucket所在地域。
            String region = "cn-beijing";

            // 创建OSSClient实例。
            // 当OSSClient实例不再使用时，调用shutdown方法以释放资源。
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region(region)
                    .build();


            try {
                byte[] content = pdfBytes;
                // 创建PutObjectRequest对象。
                PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new ByteArrayInputStream(content));

                // 创建PutObject请求。
                PutObjectResult result = ossClient.putObject(putObjectRequest);

                // 指定生成的预签名URL过期时间，单位为毫秒。本示例以设置过期时间为1小时为例。
                Date expiration = new Date(new Date().getTime() + 3600 * 1000L);

                URL url = ossClient.generatePresignedUrl(bucketName, objectName,expiration);
                // 打印预签名URL。
                return url.toString();

            } catch (OSSException oe) {
                System.out.println("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                System.out.println("Error Message:" + oe.getErrorMessage());
                System.out.println("Error Code:" + oe.getErrorCode());
                System.out.println("Request ID:" + oe.getRequestId());
                System.out.println("Host ID:" + oe.getHostId());
            } catch (ClientException ce) {
                System.out.println("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                System.out.println("Error Message:" + ce.getMessage());
            }

        }
        catch (Exception e){
            System.err.println("执行失败: " + e.getMessage());
            throw new generateDownloadUrlException("下载失败Url");
        }
        return "";
    }

    @Override
    public void insertIntoScore(Integer fileId, Integer evaluatorId, Integer score) {
        if (fileId == null || evaluatorId == null || score == null) {
            throw new paramNullException("参数不能为空");
        }

        if (score <= 0 || score > 100) {
            throw new scoreErrorException("分数输入错误");
        }

        // 获取已有评分（可能为 null）
        Integer existingScore = evaluatormapper.getScore(fileId);

        if (existingScore == null) {
            // 无记录，插入
            evaluatormapper.insertScore(fileId, evaluatorId, score);
        } else {
            // 有记录，更新
            evaluatormapper.updateScore(score, fileId, evaluatorId);
        }
    }



    @Transactional
    public Integer commitBlockChain(Integer fileId, Integer score, String scoreId) {
        try {
//            String configFile = "src/main/resources/config-example.toml";
//            // 初始化BcosSDK对象
//            BcosSDK sdk = BcosSDK.build(configFile);
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("config-example.toml");
            File tempFile = File.createTempFile("config", ".toml");
            Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);

            BcosSDK sdk = BcosSDK.build(tempFile.getAbsolutePath());
            // 获取Client对象，此处传入的群组ID为1
            Client client = sdk.getClient(Integer.valueOf(1));
            // 构造AssembleTransactionProcessor对象，需要传入client对象，CryptoKeyPair对象和abi、binary文件存放的路径。abi和binary文件需要在上一步复制到定义的文件夹中。
            CryptoKeyPair keyPair = client.getCryptoSuite().createKeyPair();

            //仅交易和查询
//            AssembleTransactionProcessor transactionProcessor = TransactionProcessorFactory.createAssembleTransactionProcessor(client, keyPair, "src/main/resources/abi/", "");

            String abiDirPath = extractAbiDirToTemp();

            AssembleTransactionProcessor transactionProcessor =
                    TransactionProcessorFactory.createAssembleTransactionProcessor(
                            client,
                            keyPair,
                            abiDirPath,
                            ""
                    );

            //上链过程：

            //上链数据构造
            List<Object> params = new ArrayList<>();
            params.add(fileId.toString());
            params.add(score.toString());
            params.add(scoreId);

            //调用合约的上链函数
            TransactionResponse transactionResponse = transactionProcessor.sendTransactionAndGetResponseByContractLoader("FileScore","0x0e87fc5a3ad709071261fc58338751f46d3a3cb6","InsertScore", params);

            // 获取交易哈希
            String transactionHash = transactionResponse.getTransactionReceipt().getTransactionHash();
            System.out.println("Transaction Hash: " + transactionHash);

            evaluatormapper.saveHash3(fileId,transactionHash);


            //打印返回值
            List<Object> returnValues = transactionResponse.getReturnObject();

            if(returnValues != null){
                for(Object returnValue : returnValues){
                    System.out.println("返回值: "+ returnValue.toString());
                }
            }
        } catch (Exception e) {
            System.err.println("执行失败: " + e.getMessage());
            throw new commitChainFail("链上提交失败");
        }
        return 0;
    }

    @Override
    public Evaluator SelectById(Integer id) {
        return evaluatormapper.SelectById(id);
    }

    @Override
    public Evaluator SelectByUserName(String userName) {
        return evaluatormapper.SelectByUserName(userName);
    }


    @Override
    public PageResult<Evaluator> pageQuery(EvaluatorPageQueryDTO evaluatorPageQueryDTO) {
        // 开启分页
        PageHelper.startPage(evaluatorPageQueryDTO.getPage(), evaluatorPageQueryDTO.getPageSize());

        // 执行查询
        Page<Evaluator> page = evaluatormapper.pageQuery(evaluatorPageQueryDTO);

        // 封装结果
        return new PageResult<>(page.getTotal(), page.getResult());
    }

    @Override
    public void add(Evaluator evaluator) {
        evaluatormapper.insert(evaluator);

    }

    @Override
    public void update(Evaluator evaluator) {
        evaluatormapper.update(evaluator);

    }

    @Override
    public void deleteById(Integer id) {
        evaluatormapper.deleteById(id);

    }

    public String extractAbiDirToTemp() throws IOException {
        // 1. 创建临时目录
        Path tempDir = Files.createTempDirectory("abi-temp");

        // 2. 定义 abi 和 bin 文件的文件名（不带路径）
        String[] abiFiles = {
                "FileSummary.abi", "FileCheck.abi", "FileScore.abi", "Table.abi"
        };
        String[] binFiles = {
                "FileSummary.bin", "FileCheck.bin", "FileScore.bin", "Table.bin"
        };

        // 3. 拷贝 abi 文件
        for (String fileName : abiFiles) {
            try (InputStream in = getClass().getClassLoader().getResourceAsStream("abi/" + fileName)) {
                if (in == null) {
                    throw new FileNotFoundException("未找到资源文件：abi/" + fileName);
                }
                Files.copy(in, tempDir.resolve(fileName), StandardCopyOption.REPLACE_EXISTING);
            }
        }

        // 4. 拷贝 bin 文件
        for (String fileName : binFiles) {
            try (InputStream in = getClass().getClassLoader().getResourceAsStream("bin/" + fileName)) {
                if (in == null) {
                    throw new FileNotFoundException("未找到资源文件：bin/" + fileName);
                }
                Files.copy(in, tempDir.resolve(fileName), StandardCopyOption.REPLACE_EXISTING);
            }
        }

        return tempDir.toFile().getAbsolutePath();
    }


}
